import re
import os
from packages.ply import yacc
from packages.ply import lex
import math

class lexer(object):
    def __init__(self):
        self.tokens = (
                "ID"     ,
                "NUMBER" ,
                "POW"    , # **
                "RPAREN" ,
                "LPAREN" ,
                #"DEFINE" ,
                "HEX"    ,
                #"OTC"   ,
                "BIN"    ,
                "LSHIFT" ,
                "RSHIFT" ,
                "COMPARE",
                "EQUAL"
                )

        self.t_POW = r"\*\*"
        self.literals = '+-*/$?=:'
        self.t_RPAREN = r"\)"
        self.t_LPAREN = r"\("
        self.t_LSHIFT=r"<<"
        self.t_RSHIFT=r">>"
        #self.t_DEFINE = r"`\w+"
        self.t_COMPARE = r"[<>](?![<>])=?"
        self.t_EQUAL = r"[=!]\="

    def build(self,**kwargs):
        self.lexer = lex.lex(object=self,**kwargs)

    def token(self):
        tok = self.lexer.token()
        return tok

    def input(self,data):
        self.lexer.input(data)

    def t_error(self,t):
        self.lexer.skip(1)

    def t_ID(self,t):
        r"\b[_a-zA-Z]\w*\b"
        return t

    def t_NUMBER(self,t):
        r"\d+"
        t.value = int(t.value)
        return t
    
    def t_HEX(self,t):
        r"\d+'h[0-9a-zA-Z]+"
        return t

    def t_BIN(self,t):
        r"\d+'b[01_]+"
        return t

class expr_cal():
    def __init__(self):
        self.lexer = lexer()
        self.lexer.build()
        self.tokens = self.lexer.tokens
        self.parse = yacc.yacc(module=self,debug=False)


        #self.paraInfo = {"ADDR":12}



    precedence = (
        ("left","EQUAL"),
        ("left","COMPARE"),
        ('left', 'LSHIFT', 'RSHIFT'),
        ('left', '+', '-'),
        ('left', '*', '/'),
        ('right', "POW")
    )


    def parasync(self,parainfo):
        self.paraInfo = parainfo

    def run(self,text):
        return self.parse.parse(text,lexer=self.lexer,debug=False)

    def p_error(self,p):
        while True:
            tok = self.parse.token()
            print("error",tok)
            if not tok:
                break

    def p_expr_binop(self,p):
        """ expr : expr '+' expr
                 | expr '-' expr
                 | expr '*' expr
                 | expr '/' expr
                 | expr  POW expr
                 | expr COMPARE expr
                 | expr EQUAL expr """
        if(p[2] == "+"):
            p[0] = p[1] + p[3]
        elif(p[2] == "-"):
            p[0] = p[1] - p[3]
        elif(p[2] == "*"):
            p[0] = p[1] * p[3]
        elif(p[2] == "/"):
            p[0] = int(p[1] / p[3])
        elif(p[2] == "**"):
            p[0] = p[1] ** p[3]
        elif(p[2] == ">"):
            p[0] = p[1] > p[3]
        elif(p[2] == "<"):
            p[0] = p[1] < p[3]
        elif(p[2] == ">="):
            p[0] = p[1] >= p[3]
        elif(p[2] == "<="):
            p[0] = p[1] <= p[3]
        elif(p[2] == "=="):
            p[0] = p[1] == p[3]
        elif(p[2] == "!="):
            p[0] = p[1] != p[3]
        elif(p[2]=="<<"):
            p[0] = p[1] << p[3]
        elif(p[2]==">>"):
            p[0] = p[1] >> p[3]

    def p_expr_paren(self,p):
        "expr : LPAREN expr RPAREN "
        p[0] = p[2]

    def p_expr_id(self,p):
        " expr : ID "
        p[0] = self.paraInfo.get(p[1],self.paraInfo.get("1"+p[1],0))

    def p_expr_num(self,p):
        "expr : NUMBER"
        p[0]= p[1]

    def p_expr_bin(self,p):
        """expr : HEX
                | BIN """
        p[0] = p[1]

    def p_expr_ternary(self,p):
        "expr : expr '?' expr ':' expr "
        p[0] = p[3] if p[1] else p[5]

    def p_expr_fun(self,p):
        "expr : ID LPAREN expr RPAREN "
        if(p[1] == "clog2" or p[1]=="log2"):
            p[0] = int(math.log2(p[3]))
        else:
            p[0] = int(input("Please input the value of "+p[1]+p[2]+str(p[3])+p[4]+" :"))
    def p_expr_comfun(self,p):
        "expr : '$' ID LPAREN expr RPAREN "
        if(p[2] == "clog2"):
            p[0] = int(math.log2(p[4]))
        else:
            p[0] = int(input("Please input the value of "+p[1]+p[2]+p[3]+p[4]))

    
if __name__ == "__main__":
    p = expr_cal()
    print(p.run("(ADDR) + (3 != 0 ? 12 : 15) * clog2(16)"))

